package com.zoe.onelink.business.util.ws;

import cn.hutool.core.exceptions.ExceptionUtil;
import com.zoe.onelink.business.property.OnelinkBizProperties;
import com.zoe.onelink.business.ws.OnelinkDynamicClientFactory;
import com.zoe.onelink.common.enumeration.ExceptionCodeEnum;
import com.zoe.onelink.common.exception.ApiException;
import com.zoe.onelink.util.StrUtil;
import com.zoe.onelink.util.ValidUtil;
import lombok.RequiredArgsConstructor;
import org.apache.cxf.endpoint.Client;
import org.apache.cxf.transport.http.HTTPConduit;
import org.apache.cxf.transports.http.configuration.HTTPClientPolicy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * <p>标题: 请求第三方webService接口工具类</p>
 * <p>描述: 已废弃,该工具类仅用于兼容旧代码,最新请直接使用HuTool的SoapClient工具类</p>
 * <p>版权: Copyright (c) 2016</p>
 * <p>公司: 智业软件股份有限公司</p>
 *
 * @version: 1.0
 * @author: cxy
 * @date 2016/11/09
 */
@Deprecated
@RequiredArgsConstructor
public class WSRequestUtil {

    private static final Logger logger = LoggerFactory.getLogger(WSRequestUtil.class);

    private static Map<String, Client> CLIENT_CACHE_MAP = new HashMap<>();

    /**
     * 发送WebService的请求后等待响应的时间,超过设置的时长就认为是响应超时.以毫秒为单位,默认是60000毫秒,即60秒.
     */
    private static final int RECEIVE_TIMEOUT = 60000;
    /**
     * WebService以TCP连接为基础,这个属性可以理解为TCP握手时的时间设置,超过设置的时间就认为是连接超时.以毫秒为单位,默认是30000毫秒,即30秒。
     */
    private static final int CONNECTION_TIMEOUT = 30000;

    private final OnelinkBizProperties.BizParam BIZ_PARAM;

    public static WSRequestUtil SELF;

    public static final AtomicInteger CURRENT_TASK_COUNT = new AtomicInteger();

    @PostConstruct
    public void init() {
        SELF = this;
    }

    /**
     * 获取客户端
     *
     * @param wsUrl          ws地址
     * @param receiveTimeout 响应超时时间
     * @return Client 客户端对象
     * @throws Exception 异常信息
     */
    private static Client getClient(String wsUrl, int receiveTimeout) throws Exception {
        return getClient(wsUrl, CONNECTION_TIMEOUT, receiveTimeout);
    }


    /**
     * 获取客户端
     *
     * @param wsUrl ws地址
     * @return Client 客户端对象
     * @throws Exception 异常信息
     */
    private static Client getClient(String wsUrl, int connectionTimeout, int receiveTimeout) throws Exception {
        if (ValidUtil.isEmptyOrNull(wsUrl)) {
            return null;
        }
        Client client = CLIENT_CACHE_MAP.get(wsUrl);
        if (client == null) {
            synchronized (WSRequestUtil.class) {
                client = CLIENT_CACHE_MAP.get(wsUrl);
                // 双重检查
                if (client == null) {
                    long start = System.currentTimeMillis();
                    logger.info("CXF调用webservice生成动态客户端");
                    OnelinkDynamicClientFactory factory = OnelinkDynamicClientFactory.newInstance();
                    client = factory.createClient(wsUrl);
                    logger.info("CXF调用webservice生成动态客户端成功,耗时: {}", System.currentTimeMillis() - start);
                    //设置超时时间
                    HTTPConduit http = (HTTPConduit) client.getConduit();
                    HTTPClientPolicy httpClientPolicy = new HTTPClientPolicy();
                    httpClientPolicy.setConnectionTimeout(connectionTimeout);
                    httpClientPolicy.setAllowChunking(false);
                    httpClientPolicy.setReceiveTimeout(receiveTimeout);
                    http.setClient(httpClientPolicy);
                    CLIENT_CACHE_MAP.put(wsUrl, client);
                }
            }
        }
        return client;
    }

    /**
     * 执行请求
     *
     * @param wsUrl      url地址
     * @param methodName 方法名称
     * @param params     参数集
     * @return String 返回信息
     */
    public static Object[] doRequest(String wsUrl, String methodName, Object... params) throws Exception {
        return doRequest(wsUrl, methodName, RECEIVE_TIMEOUT, params);
    }

    /**
     * 执行请求
     *
     * @param wsUrl             url地址
     * @param methodName        方法名称
     * @param connectionTimeout 连接超时时间,单位:毫秒
     * @param receiveTimeout    接受超时时间,单位:毫秒
     * @param params            参数集
     * @return String 返回信息
     */
    public static Object[] doRequest(String wsUrl, String methodName, int connectionTimeout, int receiveTimeout, Object... params) throws Exception {
        logger.debug("请求地址：{}，请求方法：{}, 请求参数{}", wsUrl, methodName, params);
        if (ValidUtil.isEmptyOrNull(wsUrl)) {
            throw new Exception("接口地址不能为空");
        }
        if (ValidUtil.isEmptyOrNull(methodName)) {
            throw new Exception("方法名不能为空");
        }
        if (CURRENT_TASK_COUNT.incrementAndGet() > SELF.BIZ_PARAM.getWsMaxRequestCount()) {
            String msg = StrUtil.format("当前并发数超过{},请稍后再试", SELF.BIZ_PARAM.getWsMaxRequestCount());
            throw new Exception(msg);
        }
        try {
            Client client = getClient(wsUrl, connectionTimeout, receiveTimeout);
            return client.invoke(methodName, params);
        } catch (Exception e) {
            String stackTraceStr = ExceptionUtil.stacktraceToString(e);
            logger.error("WS请求失败, 请求地址：{}, 请求方法：{}, 请求参数{}, 异常信息：{}", wsUrl, methodName, params, stackTraceStr);
            throw ApiException.createEx(ExceptionCodeEnum.WS_OUTER_ERROR, e.getMessage());
        } finally {
            CURRENT_TASK_COUNT.decrementAndGet();
        }
    }

    /**
     * 执行请求
     *
     * @param wsUrl      url地址
     * @param methodName 方法名称
     * @param params     参数集
     * @return String 返回信息
     */
    public static Object[] doRequest(String wsUrl, String methodName, int receiveTimeout, Object... params) throws Exception {
        return doRequest(wsUrl, methodName, CONNECTION_TIMEOUT, receiveTimeout, params);
    }

}
